#include "debug.h"
#include "Utils\\Rect.h"
#include "Utils\\Surface.h"

NS_BEGIN(SDL)
NS_BEGIN(UTILS)

#pragma region CTOR & DTOR

Surface::Surface(SDL_Surface* _surface, bool _dontFree)
	: m_surface(_surface), m_dontFree(_dontFree)
{
	CTOR("Surface", this);
}

Surface::Surface(Surface const& _surface)
{
	CTOR("Surface", this);
	*this = _surface;
}

Surface::~Surface()
{
	DTOR("Surface", this);
	if (m_dontFree == false && m_surface != NULL)
		SDL_FreeSurface(m_surface);
}

#pragma endregion
#pragma region Operators

Surface& Surface::operator=(Surface const& _surface)
{
	if (this != &_surface)
	{
		m_dontFree = false;
		m_surface = Surface::DuplicateSurface(_surface.m_surface);
	}
	return (*this);
}

bool Surface::operator==(Surface const& _surface) const
{
	SDL_Surface* surface = ((Surface*)&_surface)->GetSurface();
	if (m_surface == NULL && surface != NULL)
		return (false);
	if (m_surface != NULL && surface == NULL)
		return (false);
	if (m_surface == NULL && surface == NULL)
		return (true);
	if (m_surface->w != _surface.GetW() || m_surface->h != _surface.GetH())
		return (false);

	for (int y = 0; y < m_surface->h; y++)
		for (int x = 0; x < m_surface->w; x++)
			if (this->GetPixel(x, y) != _surface.GetPixel(x, y))
				return (false);
	return (true);
}

bool Surface::operator!=(Surface const& _surface) const
{
	SDL_Surface* surface = ((Surface*)&_surface)->GetSurface();
	if (m_surface == NULL && surface != NULL)
		return (true);
	if (m_surface != NULL && surface == NULL)
		return (true);
	if (m_surface == NULL && surface == NULL)
		return (false);
	if (m_surface->w != _surface.GetW() || m_surface->h != _surface.GetH())
		return (true);

	for (int y = 0; y < m_surface->h; y++)
		for (int x = 0; x < m_surface->w; x++)
			if (this->GetPixel(x, y) != _surface.GetPixel(x, y))
				return (true);
	return (false);
}

#pragma endregion

int Surface::BlitSurface(SDL_Surface* _src, Rect const* _srcRect,
						 SDL_Surface* _dst, Rect const* _dstRect)
{
	SDL_Rect copySrc, copyDst;

	SDL_Rect* srcRect = NULL;
	if (_srcRect != NULL && _srcRect->w != 0 && _srcRect->h != 0)
		srcRect = (SDL_Rect*)&_srcRect->GetRect();
	SDL_Rect* dstRect = NULL;
	if (_dstRect != NULL)
		dstRect = (SDL_Rect*)&_dstRect->GetRect();

	if (srcRect != NULL)
		copySrc = *srcRect;
	if (dstRect != NULL)
		copyDst = *dstRect;
	return (SDL_BlitSurface(_src, (srcRect != NULL) ? &copySrc : NULL,
							_dst, (dstRect != NULL) ? &copyDst : NULL));
}

SDL_Surface* Surface::DuplicateSurface(SDL_Surface* _surface)
{
	Uint32 colorkey = _surface->format->colorkey;
	Uint32 alpha = _surface->format->alpha;
	Surface::SetColorKey(_surface, 0);
	Surface::SetAlpha(_surface, SDL_ALPHA_OPAQUE);

	SDL_Surface* surface = SDL_CreateRGBSurface(
		_surface->flags, _surface->w, _surface->h, _surface->format->BitsPerPixel,
		_surface->format->Rmask, _surface->format->Gmask, _surface->format->Bmask,
		_surface->format->Amask
		);
	Surface::BlitSurface(_surface, NULL, surface, NULL);

	Surface::SetAlpha(_surface, alpha);
	Surface::SetAlpha(surface, alpha);
	Surface::SetColorKey(_surface, colorkey);
	Surface::SetColorKey(surface, colorkey);
	return (surface);
}

int Surface::SetColorKey(SDL_Surface* _surface, Uint32 _colorkey)
{
	if (_surface == NULL)
		return (-1);
	return (SDL_SetColorKey(_surface, SDL_SRCCOLORKEY, _colorkey));
}

int Surface::SetAlpha(SDL_Surface* _surface, Uint8 _alpha)
{
	return (SDL_SetAlpha(_surface, SDL_SRCALPHA, _alpha));
}

// Function from SDL doc
Uint32 Surface::GetPixel(SDL_Surface* _surface, Uint32 _x, Uint32 _y)
{
	SDL_LockSurface(_surface);
	int bpp = _surface->format->BytesPerPixel;
	/* Here p is the address to the pixel we want to retrieve */
	Uint8 *p = (Uint8 *)_surface->pixels + _y * _surface->pitch + _x * bpp;
	Uint32 result = 0;
	switch (bpp)
	{
	case 1:
		result = *p;
		break ;
	case 2:
		result = *(Uint16 *)p;
		break ;
	case 3:
		if (SDL_BYTEORDER == SDL_BIG_ENDIAN)
			result = p[0] << 16 | p[1] << 8 | p[2];
		else
			result = p[0] | p[1] << 8 | p[2] << 16;
		break ;
	case 4:
		result = *(Uint32 *)p;
		break ;
	default:
		break ;		/* shouldn't happen, but avoids warnings */
	}
	SDL_UnlockSurface(_surface);
	return (result);
}

// Function from SDL doc
void Surface::SetPixel(SDL_Surface* _surface, Uint32 _pixel,
					   Uint32 _x, Uint32 _y)
{
	SDL_LockSurface(_surface);
	int bpp = _surface->format->BytesPerPixel;
	/* Here p is the address to the pixel we want to set */
	Uint8 *p = (Uint8 *)_surface->pixels + _y * _surface->pitch + _x * bpp;
	switch (bpp)
	{
	case 1:
		*p = _pixel;
		break;
	case 2:
		*(Uint16 *)p = _pixel;
		break;
	case 3:
		if (SDL_BYTEORDER == SDL_BIG_ENDIAN) {
			p[0] = (_pixel >> 16) & 0xff;
			p[1] = (_pixel >> 8) & 0xff;
			p[2] = _pixel & 0xff;
		}
		else {
			p[0] = _pixel & 0xff;
			p[1] = (_pixel >> 8) & 0xff;
			p[2] = (_pixel >> 16) & 0xff;
		}
		break;
	case 4:
		*(Uint32 *)p = _pixel;
		break;
	default:
		break;		/* shouldn't happen, but avoids warnings */
	}
	SDL_UnlockSurface(_surface);
}

int Surface::BlitSurface(Rect const* _srcRect, SDL_Surface* _dst,
						 Rect const* _dstRect)
{
	return (Surface::BlitSurface(m_surface, _srcRect, _dst, _dstRect));
}

SDL_Surface* Surface::DuplicateSurface() const
{
	return (Surface::DuplicateSurface(m_surface));
}

int Surface::SetColorKey(Uint32 _colorkey)
{
	return (Surface::SetColorKey(m_surface, _colorkey));
}

int Surface::SetAlpha(Uint8 _alpha)
{
	return (Surface::SetAlpha(m_surface, _alpha));
}

Uint32 Surface::GetPixel(Uint32 _x, Uint32 _y) const
{
	return (Surface::GetPixel(m_surface, _x, _y));
}

void Surface::SetPixel(Uint32 _pixel, Uint32 _x, Uint32 _y)
{
	return (Surface::SetPixel(m_surface, _pixel, _x, _y));
}

#pragma region Getters & Setters

SDL_Surface* Surface::GetSurface()
{
	return (m_surface);
}

void Surface::SetSurface(SDL_Surface* _surface)
{
	if (m_dontFree == false && m_surface != NULL)
		SDL_FreeSurface(m_surface);
	m_surface = _surface;
}

SDL_PixelFormat* Surface::GetPixelFormat()
{
	if (m_surface == NULL)
		return (NULL);
	return (m_surface->format);
}

int Surface::GetW() const
{
	if (m_surface == NULL)
		return (0);
	return (m_surface->w);
}

int Surface::GetH() const
{
	if (m_surface == NULL)
		return (0);
	return (m_surface->h);
}

bool Surface::GetDontFree() const
{
	return (m_dontFree);
}

void Surface::SetDontFree(bool _value)
{
	m_dontFree = _value;
}

#pragma endregion

NS_END(UTILS)
NS_END(SDL)